/*
 * Copyright 2008 Max Kugland
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.splink.resource {
	import org.splink.library.distributor.IDistributor;
	import org.splink.library.queue.QEvent;
	import org.splink.library.queue.Qable;
	import org.splink.library.utils.ArrayUtils;
	import org.splink.resource.resourcebundles.LibrariesData;
	import org.splink.resource.resourcebundles.ResourcebundleData;
	import org.splink.resource.resourcebundles.ResourcebundlesData;
	import org.splink.resource.resourcebundles.libraries.LibraryData;
	import org.splink.resource.resourcebundles.resourcebundle.locales.LocaleData;

	import flash.system.ApplicationDomain;
	import flash.system.LoaderContext;

	/**
	 * @inheritDoc
	 * 
	 * Boots splinkresource according to the given <code>ResourcebundlesData</code>
	 * 
	 * @author Max Kugland
	 */
	final public class ResourceProcessor extends Qable implements IResourceProcessor {
		private var _locale : String;
		private var _loaderContext : LoaderContext;
		private var _provider : ResourceProvider;
		private var _data : ResourcebundlesData;
		private var _loader : ResourcebundleLoader;

		/**
		 * @param data the <code>ResourcebundlesData</code> object which represents the resourcebundles configuration xml
		 * @param locale the locale of the resourcebundle to load. If no valid locale is given the
		 * configured defaultLocale is used instead
		 */
		public function ResourceProcessor(data : ResourcebundlesData, locale : String = "defaultLocale") {
			super(id);
			_data = data;
			_locale = locale;
		}

		/**
		 * @inheritDoc
		 */
		public function loaderContext(loaderContext : LoaderContext) : IResourceProcessor {
			_loaderContext = loaderContext;
			return this;
		}

		/**
		 * @inheritDoc
		 */
		override protected function doStart() : void {
			_provider = ResourceProvider(ResourceProvider.instance());
			if(_locale == null || _locale == "defaultLocale") _locale = _data.defaultLocale;			var bundle : ResourcebundleData = getResourcebundle(_data, _locale);

			_provider.setCurrentLocale(_locale);
			_provider.setDefaultLocale(_data.defaultLocale);
			_provider.setAvailableLocales(getAvailableLocales(_data));
			
			// load the libraries only if they aren't already loaded
			if(_provider._libraries && _data.librariesData) {
				for each (var lib : LibraryData in _data.librariesData.libraryDataArray) {
					if(_provider.getLibraryById(lib.id) == null) {
						loadBundle(bundle, _data.librariesData);
						return;
					}
				}
			} else {
				loadBundle(bundle, _data.librariesData);
				return;
			}
			
			loadBundle(bundle, null);
		}
		
		private function loadBundle(bundle : ResourcebundleData, libraries : LibrariesData) : void {
			_loaderContext = _loaderContext ? _loaderContext : new LoaderContext(false, ApplicationDomain.currentDomain);
			
			var scope : IDistributor = this;
			_loader = new ResourcebundleLoader(bundle, libraries, _loaderContext);
			_loader.register(QEvent.COMPLETE, function(e : QEvent):void {
				complete();
			});
			_loader.register(QEvent.ERROR, function(e : QEvent):void {
				error(e.errorMessage);
			});
			_loader.register(QEvent.PROGRESS, function(e : QEvent):void {
				distribute(e.clone(scope));
			});
			_loader.register(ResourceProcessorEvent.PROGRESS, function(e : ResourceProcessorEvent):void {
				distribute(e.clone(scope));
			});
			
			_loader.start();
		}

		/**
		 * @inheritDoc
		 */
		override protected function doStop() : void {
			if(_loader) _loader.stop();
		}

		/**
		 * @inheritDoc
		 */
		override protected function doReset() : void {
			if(_loader) _loader.reset();
		}

		/**
		 * @inheritDoc
		 */
		override protected function doFinalize() : void {
			if(_loader) {
				_loader.finalize();
				_loader = null;
			}
			_loaderContext = null;
			_data = null;
			_provider = null;
		}

		/**
		 * @return the <code>ResourcebundleData</code> which corresponds to the given locale.
		 * if no matching bundle can be found the bundle which belongs to the default locale is returned 
		 */
		private function getResourcebundle(data : ResourcebundlesData, locale : String) : ResourcebundleData {
			var bundle : ResourcebundleData = data.getResourcebundleDataByLocale(locale);
			if(bundle == null) {
				bundle = getMappedLocaleBundle(data, locale);
				if(bundle == null) {
					bundle = data.getResourcebundleDataByLocale(data.defaultLocale);
					_locale = data.defaultLocale;
				}
			}
			return bundle;
		}

		/**
		 * Checks whether the given locale can be found within the additional locales defined
		 * for a resource bundle and returns an instance of the the enclosing resourcebundle data. 
		 */
		private static function getMappedLocaleBundle(data : ResourcebundlesData, locale : String) : ResourcebundleData {
			for each (var rb : ResourcebundleData in data.resourcebundleDataArray) {
				if(rb.localesData) {
					for each (var ld : LocaleData in rb.localesData.localeDataArray) {
						if(ld.value == locale) {
							return rb;
						}
					}
				}
			}
			return null;
		}

		private static function getAvailableLocales(data : ResourcebundlesData) : Array {
			var ar : Array = new Array();
			for each (var rb : ResourcebundleData in data.resourcebundleDataArray) {
				if(rb.localesData) {
					for each (var ld : LocaleData in rb.localesData.localeDataArray) {
						ar.push(ld.value);
					}
				}
				ar.push(rb.locale);
			}
			
			return ArrayUtils.removeDuplicates(ar);
		}
	}
}
